#!/usr/bin/env python
import logging
import os
import time
from sys import argv
from sys import exit
from ConfigParser import NoSectionError
#Local imports
from run import get_configuration
from config import ConfigValues
from config import split_strip
from loghelper import add_logfile
from loghelper import init_logging
from experimenthelper import read_env
from executionhelper import safe_exec_fork
from executionhelper import safe_exec

# should be run from within lxc
def start_module(nodeid, module, config, log, environment, background=True):
	path=config.get('directories','modules') + '/' + module
	if not os.path.isfile(path):
		log.debug('Module is configured through settings')
		path = '{}/{}'.format(config.get('directories','modules'), config.get(module, 'executable').strip())
	log.info("Starting module %s", path)

	log.debug("Setting environment variables for module %s", module)
	try:
		for k in config.items(module):
	#		safe_exec(['/bin/sh export',k[0]+'="' + k[1] + '"'],use_shell=True)
			environment[int(nodeid)][k[0]] = str(k[1]).format(node_id=nodeid)
	except NoSectionError:
		log.debug('Could not find section for module %s', module)
	except Exception,err:
		log.exception(err)

	log.debug("Executing module")
	try:
		if background:
			log.debug('Starting background module %s', path)
			safe_exec_fork([path,'start'], log, config, environment, nodeid,chdir=True)
		else:
			log.debug('Starting sequential module %s', path)
			safe_exec([path, 'start'], log, config, environment, nodeid, chdir=True)
	except:
		log.exception("Unable to start module %s", path)
		raise

# should be run within lxc
def start_all_background_modules(nodeid, modules, config, log, env):
	for mod in modules:
		start_module(nodeid, mod, config, log, env)
		
def start_sequential_modules(nodeid, modules, config, log, env):
	total_sleep = 0
	for module in modules:
		if config.has_section(module) and config.has_option(module, 'sleep'):
			sleepy_time = config.getint(module, 'sleep')
			time.sleep(sleepy_time/2)
			total_sleep += sleepy_time
			start_module(nodeid, module, config, log, env, False)
			time.sleep(sleepy_time/2)
		else:
			start_module(nodeid, module, config, log, env, False)
	return total_sleep

def stop_all_background_modules(modules, log, config, env, nodeid):
	path = config.get('directories','modules')
	for mod in modules:
		try:
			executable = path + '/' + mod
			if not os.path.isfile(executable):
				executable = '{}/{}'.format(path, config.get(mod, 'executable').strip())
			safe_exec([executable, 'stop'], log, config, env, nodeid ,chdir=True)
		except:
			log.exception("Unable to stop module %s", executable)
			#raise

if __name__ == '__main__':
	nodeid = argv[1]
	experiment = argv[2]
	config_filename = argv[3]
	log_name = "node" + nodeid
	conf, env = get_configuration('{}.init'.format(log_name), config_filename)
	
	initial_wait = conf[ConfigValues.INITIAL_WAIT]
	duration = conf[ConfigValues.DURATION]
	shutdown_wait = conf[ConfigValues.SHUTDOWN_WAIT]
	
	config = conf[ConfigValues.CFG]
	experiment_path = config.get('directories','experiments')
	experiment_script = config.get(experiment,'experiment')
	sequential_modules = split_strip(',', config.get(experiment,'modules'))
	background_modules = split_strip(',', config.get(experiment, 'background_modules'))
	
	#We have to initialize logging a new because this is executed in a different interpereter
	init_logging()
	log = logging.getLogger(log_name)
	add_logfile(log, config.get('directories','dumps') + "/" + log_name + ".log")
	log.info("Starting node %s", nodeid)

	orig_dir = os.getcwd()
	#use_chroot = conf[ConfigValues.USE_CHROOT]
	#if use_chroot:
	#	chroot_bindpoint = config.get('directories','chroot_bindpoint')
	#	log.info("In chroot, changing directory to %s", chroot_bindpoint)
	#	orig_dir = os.getcwd()
	#	os.chdir(chroot_bindpoint)

	
	environment = read_env(config, nodeid, False)

	try:
		log.info("Starting sequential modules...")
		module_sleep = start_sequential_modules(nodeid, sequential_modules, config, log, environment)
		
		log.info('Starting background modules...')
		start_all_background_modules(nodeid, background_modules, config, log, environment)

		log.info("Waiting for %s", initial_wait)
		time.sleep(initial_wait - module_sleep)

		log.info("Running experiment %s", experiment_script)
		process = safe_exec_fork([experiment_path+'/'+experiment_script,nodeid], log, config, environment, nodeid, chdir=True)
		time.sleep(duration)

		log.info("Waiting for shutdown...")
		time.sleep(shutdown_wait/2)

		if process.is_alive():
			log.warning("Process has not terminated after %s seconds. Sending SIGTERM", (shutdown_wait/2))
			process.terminate()
			time.sleep(shutdown_wait/4)

		if process.is_alive():
			log.error("Process still running. Attempting SIGKILL.")
			os.kill(process.pid,signal.SIGKILL)
			time.sleep(shutdown_wait/4)

		if process.is_alive():
			log.error("Unable to terminate experiment. Exiting anyway.")
		else:
			log.info("Experiment is no longer running.")

		log.info("Stopping background modules...")
		stop_all_background_modules(background_modules, log, config, environment, nodeid)

		log.info("Done, exiting")
	except Exception,err:
		log.exception(str(err))
		log.error("NODE CRASHED - STOPPING MODULES")
		stop_all_background_modules(background_modules, log, config, environment, nodeid)
		raise

	if use_chroot:
		os.chdir(orig_dir)
	exit()